//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-06-06     ZhangYihua   first version
//
// Description  : synchronization between different clock domain for reducing metastable state probability
//################################################################################

module sync_dff #(
parameter           SYNC_NUM                = 3,            // synchronization pipeline number for each bit
parameter           BW                      = 8,            // bit width
parameter [BW-1:0]  INI                     = {BW{1'b0}}
) ( 
input                                       rst_n,
input                                       clk,

input               [BW-1:0]                d,      // based on raw clock domain, forbid combinational logic input
output              [BW-1:0]                q       // based on clk domain, synchronization is completed
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                [BW-1:0]                q_meta[SYNC_NUM:0];

//################################################################################
// main
//################################################################################

// synopsys translate_off
`ifdef SYNC_DFF_INSERT_RANDOM_DELAY     // for EDA simulation only
localparam          DIV32_NUM               = (BW-1)/32+1;

reg                 [BW-1:0]                d_hold;
reg                 [BW-1:0]                d_delay;
reg                 [BW-1:0]                src_tog;
reg                 [BW-1:0]                dst_tog;
wire                [BW-1:0]                delay_sel;
reg                 [DIV32_NUM*32-1:0]      rnd;

initial begin
    d_hold  = INI;
    d_delay = INI;

    src_tog = {BW{1'b0}};
    dst_tog = {BW{1'b0}};
end

always@(d) begin:DLY
    integer         i;

    d_delay = d_hold;
    d_hold  = d;

    for (i=0; i<DIV32_NUM; i=i+1) begin
        rnd[i*32+:32] = $urandom();
    end
    src_tog = rnd[0+:BW];
end

always@(posedge clk) begin
    dst_tog <=`U_DLY src_tog;
end
assign delay_sel = src_tog ^ dst_tog;

assign q_meta[0] = ((~delay_sel) & d) | (delay_sel & d_delay);

`else
// synopsys translate_on

assign q_meta[0] = d;

// synopsys translate_off
`endif
// synopsys translate_on

genvar gi;
genvar gj;
generate 
for (gi=0; gi<SYNC_NUM; gi=gi+1) begin:G_NUM
    for (gj=0; gj<BW; gj=gj+1) begin:G_BW

        meta_dff_wrap #(
                .INI        (INI[gj]            )
        ) u_meta_dff ( 
                .rst_n      (rst_n              ),
                .clk        (clk                ),

                .d          (q_meta[gi  ][gj]   ),
                .q          (q_meta[gi+1][gj]   )
        );

    end 
end 
endgenerate

assign q = q_meta[SYNC_NUM];

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
